package com.pcbly.web.components.utils.fetion;

import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.ByteBuffer;
import java.security.MessageDigest;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.codec.binary.Hex;
import org.apache.commons.httpclient.DefaultHttpMethodRetryHandler;
import org.apache.commons.httpclient.Header;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.StringRequestEntity;
import org.apache.commons.httpclient.params.HttpConnectionManagerParams;
import org.apache.commons.httpclient.params.HttpMethodParams;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

/**
 * 怎么改都可以,请留下作者名字谢谢 @ my fetion number 585971431
 * 
 * @author Azure Yuan (email:azure1489@gmail.com QQ:304946629)
 * @since 1.0
 * 
 */
public class Fetion implements IFetion {

	public static final String FETION_SYSTEM_CONFIG_URL = "http://nav.fetion.com.cn/nav/getsystemconfig.aspx";
	public static final String FETION_URL = "http://221.130.45.203/ht/sd.aspx";
	public static final String FETION_SIPC = "221.130.45.203:8080";
	public static final String FETION_LOGIN_URL = "https://nav.fetion.com.cn/ssiportal/SSIAppSignIn.aspx";
	// public static final String FETION_LOGIN_URL =
	// "https://www.fetion.com.cn/account/login";
	public static final String FETION_SIPP = "SIPP";

	public List<FetionFriend> friends; // 所有的好友

	private static String httpTunnel = "HTTP://221.130.46.141/ht/sd.aspx";
	// private static String sipcProxy = "221.130.46.141:8080";

	private String mobile;
	private String password;

	private String ssic;

	private String sid;
	private String domain;

	private String guid;

	private int call;

	private int seq;

	private String uri;

	private HashMap<String, String> buddys;

	public Fetion(String mobile, String password) {
		this.mobile = mobile;
		this.password = password;
	}

	/**
	 * 获取响应结果
	 * 
	 * @param telephone
	 * @return
	 */
	public void getSystemConfig() {
		HttpClient client = new HttpClient();
		// 使用POST方式提交数据
		PostMethod postMethod = new PostMethod(FETION_SYSTEM_CONFIG_URL);
		StringBuffer dataBuf = new StringBuffer("<config><user mobile-no=\"")
				.append(mobile)
				.append(
						"\" /><client type=\"PC\" version=\"3.2.0540\" platform=\"W5.1\" /><servers version=\"0\" /><service-no version=\"0\" /><parameters version=\"0\" /><hints version=\"0\" /><http-applications version=\"0\" /></config>");

		InputStream is = null;
		try {
			postMethod.setRequestEntity(new StringRequestEntity(dataBuf
					.toString(), "text/xml", "utf-8"));
			client.executeMethod(postMethod);
			if (postMethod.getStatusCode() != HttpStatus.SC_OK) {
				System.out.println("认证失败!");
			} else {
				System.out.println("开始解析数据!!!!!!!!!!!!!!!");
				is = postMethod.getResponseBodyAsStream();
				SAXReader sr = new SAXReader();
				Document document = sr.read(is);
				// 获取根元素
				Element root = document.getRootElement();

				Element servers = root.element("servers");
				httpTunnel = servers.element("http-tunnel").getText();
				// sipcProxy = servers.element("sipc-proxy").getText();

				// System.out.println("httpTunnel:" + httpTunnel +
				// ",  sipcProxy:" + sipcProxy);
			}

		} catch (Exception e) {
			e.printStackTrace();
			SimpleDateFormat sd = new SimpleDateFormat("yyyy-MM-dd HH:mm");
			System.out.println("进行认证异常时间TIME************"
					+ sd.format(new Date()));
		} finally {
			// 资源的释放
			if (is != null) {
				try {
					is.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			if (postMethod != null) {
				postMethod.releaseConnection();
			}
		}

	}

	public boolean logout() {
		HashMap<String, String> fields = new HashMap<String, String>();
		fields.put("F", sid);
		fields.put("I", "1");
		fields.put("Q", "3 R");
		fields.put("X", "0");
		String msg = createSipData("R fetion.com.cn SIP-C/2.0", fields, null);
		String _url = nextUrl("s");
		String response = sendRequest(_url, msg);
		System.out.println("退出:" + response);
		// if(response.indexOf("Send SMS OK")>0)
		// return true;
		// else
		return false;
	}

	public boolean login() {
		// 登陆
		HttpClient client = new HttpClient();

		HttpConnectionManagerParams managerParams = client
				.getHttpConnectionManager().getParams();
		// 设置连接超时时间(单位毫秒)
		managerParams.setConnectionTimeout(300000);
		// 设置读数据超时时间(单位毫秒)
		managerParams.setSoTimeout(120000);

		InputStream is = null;
		PostMethod postMethod = null;
		Matcher matcher = null;

		boolean result = false;

		try {
			postMethod = new PostMethod(FETION_LOGIN_URL + "?username="
					+ URLEncoder.encode(mobile, "UTF-8") + "&password="
					+ URLEncoder.encode(password, "UTF-8"));

			DefaultHttpMethodRetryHandler defaultHttpMethodRetryHandler = new DefaultHttpMethodRetryHandler(
					100, false);

			postMethod.getParams().setParameter(HttpMethodParams.RETRY_HANDLER,
					defaultHttpMethodRetryHandler);

			client.executeMethod(postMethod);
			System.out.println(postMethod.getQueryString());
			System.out.println(postMethod.getStatusCode());
			if (postMethod.getStatusCode() != HttpStatus.SC_OK) {
				System.out.println("认证失败!");
			} else {

				Header[] headers = postMethod.getResponseHeaders();
				String Set_Cookie = headers[4].getValue();
				// System.out.println("Set_Cookie:" + Set_Cookie);
				Pattern ssicPattern = Pattern.compile("ssic=(.*?);");
				matcher = ssicPattern.matcher(Set_Cookie);

				if (matcher.find()) {
					ssic = matcher.group(1);
				}

				is = postMethod.getResponseBodyAsStream();
				SAXReader sr = new SAXReader();
				Document document = sr.read(is);
				// 获取根元素
				Element root = document.getRootElement();
				// String status_code =
				// root.attribute("status-code").getValue();
				Element user = root.element("user");
				uri = user.attribute("uri").getValue();
				user.attribute("mobile-no").getValue();
				user.attribute("user-status").getValue();

				Pattern sidPattern = Pattern.compile("sip:(\\d+)@(.+);");

				matcher = sidPattern.matcher(uri);

				if (matcher.find()) {
					sid = matcher.group(1);
					domain = matcher.group(2);
				}

				if (httpRegister()) {
					result = true;
				}
			}

		} catch (Exception e) {
			e.printStackTrace();
			SimpleDateFormat sd = new SimpleDateFormat("yyyy-MM-dd HH:mm");
			System.out.println("进行认证异常时间TIME************"
					+ sd.format(new Date()));

		} finally {
			// 资源的释放
			if (is != null) {
				try {
					is.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			if (postMethod != null) {
				postMethod.releaseConnection();
			}
		}

		return result;
	}

	private String nextUrl(String t) {
		// t = "s";
		seq += 1;

		return new StringBuffer(httpTunnel).append("?t=").append(t).append(
				"&i=").append(seq).toString();
	}

	private int nextCall() {
		call += 1;
		return call;
	}

	private String createSipData(String invite, HashMap<String, String> fields,
			String arg) {
		StringBuffer sip = new StringBuffer(invite).append("\r\n");
		for (Map.Entry<String, String> entry : fields.entrySet()) {
			sip.append(entry.getKey()).append(": ").append(entry.getValue())
					.append("\r\n");
		}
		if (arg != null) {
			try {
				sip.append("L: ").append(arg.getBytes("UTF-8").length).append(
						"\r\n\r\n").append(arg);
			} catch (UnsupportedEncodingException e) {
				return null;
			}
		}

		return sip.toString();
	}

	public String calcCnonce() {
		MessageDigest md5 = null;
		String md5Str = null;
		try {
			md5 = MessageDigest.getInstance("MD5");
			md5.update(UUID.randomUUID().toString().getBytes("UTF-8"));
			md5Str = new String(Hex.encodeHex(md5.digest())).toUpperCase();
		} catch (Exception e) {
			return null;
		}
		return md5Str;
	}

	private String calcResponse(String nonce, String cnonce) {

		String temp = ":";
		String strTemp = "REGISTER";
		char[] saltChars = { 0x77, 0x7A, 0x6D, 0x03 };
		String salt = String.valueOf(saltChars);

		String h3 = null;
		try {
			int sidLength = sid.getBytes("UTF-8").length;
			int domainLength = domain.getBytes("UTF-8").length;
			int nonceLength = nonce.getBytes("UTF-8").length;
			int tempLength = temp.getBytes("UTF-8").length;
			int strTempLength = strTemp.getBytes("UTF-8").length;
			int cnonceLength = cnonce.getBytes("UTF-8").length;

			ByteBuffer buf = null;

			// 晕,这部有点多余可以省略,就是把原来加密过的字符串转成字节
			// byte[] decode =
			// Hex.decodeHex(binstr.toUpperCase().toCharArray());

			MessageDigest sha1 = MessageDigest.getInstance("SHA-1");

			// 密码使用SHA-1加密
			sha1 = MessageDigest.getInstance("SHA-1");

			sha1.update(password.getBytes("UTF-8"));
			byte[] src = sha1.digest();
			// 加密后把{ 0x77, 0x7A, 0x6D, 0x03 }这四个字符加上用SHA-1加密过的密码
			buf = ByteBuffer.allocate(src.length
					+ salt.getBytes("UTF-8").length);
			buf.put(salt.getBytes("UTF-8"));
			buf.put(src);
			buf.flip();
			sha1.update(buf);
			buf.clear();
			// 然后再把相加过的`字节数组`用SHA-1加密
			byte[] decode = sha1.digest();

			// 用SHA-1加密```形式如 sid:domain:decode
			buf = ByteBuffer.allocate(sidLength + domainLength + decode.length
					+ (tempLength * 2));
			buf.put(sid.getBytes("UTF-8"));
			buf.put(temp.getBytes("UTF-8"));
			buf.put(domain.getBytes("UTF-8"));
			buf.put(temp.getBytes("UTF-8"));
			buf.put(decode);
			buf.flip();
			sha1.update(buf);

			byte[] keyBytes = sha1.digest();
			// 用MD5加密```形式如 key:nonce:cnonce 上面生成的就是key 生成h1
			MessageDigest md5 = MessageDigest.getInstance("MD5");
			buf = ByteBuffer.allocate(keyBytes.length + nonceLength
					+ cnonceLength + (tempLength * 2));
			buf.put(keyBytes);
			buf.put(temp.getBytes("UTF-8"));
			buf.put(nonce.getBytes("UTF-8"));
			buf.put(temp.getBytes("UTF-8"));
			buf.put(cnonce.getBytes("UTF-8"));
			buf.flip();
			md5.update(buf);
			buf.clear();

			String h1 = new String(Hex.encodeHex(md5.digest())).toUpperCase();

			// 用MD5加密```形式如 REGISTER:sid 生成h2
			buf = ByteBuffer.allocate(sidLength + strTempLength + tempLength);
			buf.put(strTemp.getBytes("UTF-8"));
			buf.put(temp.getBytes("UTF-8"));
			buf.put(sid.getBytes("UTF-8"));
			buf.flip();
			md5.update(buf);
			buf.clear();

			String h2 = new String(Hex.encodeHex(md5.digest())).toUpperCase();

			// 用MD5加密```形式如 h1:nonce:h2 生成h2
			buf = ByteBuffer.allocate(h1.getBytes("UTF-8").length + nonceLength
					+ h2.getBytes("UTF-8").length + (tempLength * 2));
			buf.put(h1.getBytes("UTF-8"));
			buf.put(temp.getBytes("UTF-8"));
			buf.put(nonce.getBytes("UTF-8"));
			buf.put(temp.getBytes("UTF-8"));
			buf.put(h2.getBytes("UTF-8"));
			buf.flip();
			md5.update(buf);
			buf.clear();

			h3 = new String(Hex.encodeHex(md5.digest())).toUpperCase();

		} catch (Exception e) {
			e.printStackTrace();
		}

		return h3;

	}

	/**
	 * 得到好友信息：包括电话号码
	 * 
	 * @return
	 */
	public List<FetionFriend> getFetionFriends() {

		List<String> list = getContactsList();
		getFriendsWithoutNumber(); // 得到好友的分组信息，以及没有电话号码的其他信息

		List<FetionFriend> result = null;

		if (list != null) {
			StringBuffer arg = new StringBuffer(
					"<args><contacts attributes=\"all\">");
			for (String uri : list) {
				arg.append("<contact uri=\"").append(uri).append("\" />");
			}
			arg.append("</contacts></args>");

			int _call = nextCall();
			HashMap<String, String> fields = new HashMap<String, String>();
			fields.put("F", sid);
			fields.put("I", String.valueOf(_call));
			fields.put("Q", "1 S");
			fields.put("N", "GetContactsInfo");
			String msg = createSipData("S fetion.com.cn SIP-C/2.0", fields, arg
					.toString())
					+ FETION_SIPP;

			String _url = nextUrl("s");
			sendRequest(_url, msg);
			_url = nextUrl("s");
			String response = sendRequest(_url, FETION_SIPP);
			String xml = response.split("<results>")[1].split("</results>")[0];
			FriendUtil f = new FriendUtil();
			try {
				result = f.getDetailFriendsFromXml(this.friends, xml);
			} catch (Exception e) {
				e.printStackTrace();
			}

		}
		return result;

	}

	/**
	 * 得到所有的好友，但是没有号码
	 * 
	 * @return
	 */
	public void getFriendsWithoutNumber() {
		String arg = "<args><contacts><buddy-lists /><buddies attributes=\"all\" /><mobile-buddies attributes=\"all\" /><chat-friends /><blacklist /></contacts></args>";
		int _call = nextCall();
		HashMap<String, String> fields = new HashMap<String, String>();
		fields.put("F", sid);
		fields.put("I", String.valueOf(_call));
		fields.put("Q", "1 S");
		fields.put("N", "GetContactList");
		String msg = createSipData("S fetion.com.cn SIP-C/2.0", fields, arg)
				+ FETION_SIPP;
		String _url = nextUrl("s");
		sendRequest(_url, msg);
		_url = nextUrl("s");
		String response = sendRequest(_url, FETION_SIPP);

		String contens[] = response.split("<results>");
		String xml = contens[1].split("</results>")[0];

		FriendUtil util = new FriendUtil();
		try {
			util.getFriendsFromXml(xml);
		} catch (Exception e) {
			e.printStackTrace();
		}

		this.friends = util.friends;

	}

	public List<String> getContactsList() {
		String arg = "<args><contacts><buddy-lists /><buddies attributes=\"all\" /><mobile-buddies attributes=\"all\" /><chat-friends /><blacklist /></contacts></args>";
		int _call = nextCall();
		HashMap<String, String> fields = new HashMap<String, String>();
		fields.put("F", sid);
		fields.put("I", String.valueOf(_call));
		fields.put("Q", "1 S");
		fields.put("N", "GetContactList");
		String msg = createSipData("S fetion.com.cn SIP-C/2.0", fields, arg)
				+ FETION_SIPP;
		String _url = nextUrl("s");
		sendRequest(_url, msg);
		_url = nextUrl("s");
		String response = sendRequest(_url, FETION_SIPP);

		Pattern uriPattern = Pattern.compile("uri=\"(sip[^\"]+)\"");
		Matcher matcher = uriPattern.matcher(response);
		List<String> list = new ArrayList<String>();
		while (matcher.find()) {
			list.add(matcher.group(1));
		}
		if (list.size() == 0)
			return null;
		else
			return list;
	}

	private String calcSalt() {
		// return hashPassword().substring(0, 8);
		return "777A6D03";
	}

	private String sendRequest(String url, String data) {
		if (guid == null) {
			guid = UUID.randomUUID().toString();
		}

		HttpClient client = new HttpClient();

		String response = null;
		PostMethod postMethod = null;
		try {
			postMethod = new PostMethod(url);

			postMethod.setRequestHeader("User-Agent", "IIC2.0/pc 3.1.0480");
			postMethod.setRequestHeader("Cookie", "ssic=" + ssic);
			postMethod.setRequestHeader("Content-Type",
					"application/oct-stream");
			postMethod.setRequestHeader("Pragma", "xz4BBcV" + guid);

			postMethod.setRequestEntity(new StringRequestEntity(data,
					"text/xml", "utf-8"));

			client.executeMethod(postMethod);

			if (postMethod.getStatusCode() != HttpStatus.SC_OK) {
				System.out.println("认证失败!");
				return null;
			} else {
				response = new String(postMethod.getResponseBodyAsString()
						.getBytes("ISO-8859-1"), "utf-8");
				// System.out.println(response);
			}

		} catch (Exception e) {
			e.printStackTrace();
			SimpleDateFormat sd = new SimpleDateFormat("yyyy-MM-dd HH:mm");
			System.out.println("进行认证异常时间TIME************"
					+ sd.format(new Date()));
			return null;
		} finally {
			// 资源的释放
			if (postMethod != null) {
				postMethod.releaseConnection();
			}
		}
		return response;

	}

	public boolean sendSms(String to, String content) {

		String toMobile = buddys.get(to);

		if (toMobile == null || "".equals(toMobile))
			return false;

		HashMap<String, String> fields = new HashMap<String, String>();
		fields.put("F", sid);
		fields.put("I", String.valueOf(nextCall()));
		fields.put("Q", "1 M");
		fields.put("T", toMobile);
		fields.put("N", "SendSMS");
		String msg = createSipData("M fetion.com.cn SIP-C/2.0", fields, content)
				+ FETION_SIPP;
		String _url = nextUrl("s");
		sendRequest(_url, msg);
		_url = nextUrl("s");
		String response = sendRequest(_url, FETION_SIPP);

		if (response.indexOf("Send SMS OK") > 0)
			return true;
		else
			return false;
	}

	public boolean sendCatSms(String to, String content) {

		String toMobile = buddys.get(to);

		if (toMobile == null || "".equals(toMobile))
			return false;

		HashMap<String, String> fields = new HashMap<String, String>();
		fields.put("F", sid);
		fields.put("I", String.valueOf(nextCall()));
		fields.put("Q", "1 M");
		fields.put("T", toMobile);
		fields.put("N", "SendCatSMS");
		String msg = createSipData("M fetion.com.cn SIP-C/2.0", fields, content)
				+ FETION_SIPP;
		String _url = nextUrl("s");
		sendRequest(_url, msg);
		_url = nextUrl("s");
		String response = sendRequest(_url, FETION_SIPP);

		if (response.indexOf("Send SMS OK") > 0)
			return true;
		else
			return false;
	}

	public void keeplogin() {
		HashMap<String, String> fields = new HashMap<String, String>();
		fields.put("F", sid);
		fields.put("I", "1");
		fields.put("Q", "3 R");
		String msg = createSipData("R fetion.com.cn SIP-C/2.0", fields, null)
				+ FETION_SIPP;
		String url = nextUrl("i");
		String response = sendRequest(url, msg);

		System.out.println("keeplogin: " + response);
	}

	private boolean httpRegister() {
		String arg = "<args><device type=\"PC\" version=\"0\" client-version=\"3.1.0480\" /><caps value=\"fetion-im;im-session;temp-group\" /><events value=\"contact;permission;system-message\" /><user-info attributes=\"all\" /><presence><basic value=\"400\" desc=\"\" /></presence></args>";

		String response = null;
		String nonce = null;

		Pattern noncePattern = Pattern.compile("nonce=\"(\\w+)\"");

		int nextCall = nextCall();

		// 1
		String url = nextUrl("i");
		response = sendRequest(url, FETION_SIPP);
		if (response == null)
			return false;

		// 2
		// ('F',self.sid), ('I',call), ('Q','1 R')
		HashMap<String, String> fields = new HashMap<String, String>();
		fields.put("F", sid);
		fields.put("I", String.valueOf(nextCall));
		fields.put("Q", "1 R");
		String msg = createSipData("R fetion.com.cn SIP-C/2.0", fields, arg)
				+ FETION_SIPP;
		url = nextUrl("s");
		response = sendRequest(url, msg);
		if (response == null)
			return false;

		// 3
		url = nextUrl("s");
		response = sendRequest(url, FETION_SIPP);
		if (response == null)
			return false;
		Matcher m = noncePattern.matcher(response);
		if (m.find()) {
			nonce = m.group(1);
		}

		// 4
		String cnonce = calcCnonce();
		String calcResponse = calcResponse(nonce, cnonce);
		String salt = calcSalt();
		fields = new HashMap<String, String>();
		fields.put("F", sid);
		fields.put("I", String.valueOf(nextCall));
		fields.put("Q", "2 R");
		StringBuffer strBuf = new StringBuffer(
				"Digest algorithm=\"SHA1-sess\",response=\"").append(
				calcResponse).append("\",cnonce=\"").append(cnonce).append(
				"\",salt=\"").append(salt).append("\"");
		fields.put("A", strBuf.toString());
		msg = createSipData("R fetion.com.cn SIP-C/2.0", fields, arg)
				+ FETION_SIPP;
		url = nextUrl("s");
		response = sendRequest(url, msg);
		if (response == null)
			return false;

		// 5
		url = nextUrl("s");
		response = sendRequest(url, FETION_SIPP);
		if (response == null)
			return false;

		return true;
	}

	public static void main(String[] args) throws Exception {

		String m = "13724285248";
		String p = "lysonggm7876";

		//
		Fetion fetion = new Fetion(m, p);
		// fetion.getSystemConfig();
		if (fetion.login()) {

			// fetion.keeplogin();

			List<FetionFriend> f2 = fetion.getFetionFriends();

			for (FetionFriend f : f2) {

				System.out.println(f);
			}

			// for (Map.Entry<String, String> entry : buddys.entrySet()) {
			// System.out.println(entry.getKey() + "," + entry.getValue());
			// }

			// if (fetion.sendSms("15902072995", "发很长很长长的中文~")) {
			// System.out.println("发送成功!");
			// } else {
			// System.out.println("发送失败!");
			// }
		} else {
			System.out.println("登录失败");
		}
	}

	public List<FetionFriend> getFriends() {
		return friends;
	}

	public void setFriends(List<FetionFriend> friends) {
		this.friends = friends;
	}

}
